#include <sdk.h>
#include <wx/tokenzr.h>
#include <M_Code_StyledTextCtrl.h>
#include <compilercommandgenerator.h>
#include <projectloader_hooks.h>
#include <tinyxml.h>
#include "nativeparser.h"
#include "classbrowser.h"
#include "parser/parser.h"
#include "parser/profiletimer.h"

#define CC_NATIVEPARSER_DEBUG_OUTPUT 0

#if defined (CC_GLOBAL_DEBUG_OUTPUT)
#if CC_GLOBAL_DEBUG_OUTPUT == 1
#undef CC_NATIVEPARSER_DEBUG_OUTPUT
#define CC_NATIVEPARSER_DEBUG_OUTPUT 1
#elif CC_GLOBAL_DEBUG_OUTPUT == 2
#undef CC_NATIVEPARSER_DEBUG_OUTPUT
#define CC_NATIVEPARSER_DEBUG_OUTPUT 2
#endif
#endif

namespace NativeParserHelper {
  class ParserDirTraverser : public wxDirTraverser {
    public:
      ParserDirTraverser( const wxString& excludePath, wxArrayString& files ) :
        m_ExcludeDir( excludePath ),
        m_Files( files ) {}

      virtual wxDirTraverseResult OnFile( const wxString& filename ) {
        if( ParserCommon::FileType( filename ) != ParserCommon::ftOther ) {
          m_Files.Add( filename );
        }
        return wxDIR_CONTINUE;
      }

      virtual wxDirTraverseResult OnDir( const wxString& dirname ) {
        if( dirname == m_ExcludeDir ) {
          return wxDIR_IGNORE;
        }
        if( m_Files.GetCount() == 1 ) {
          return wxDIR_STOP;
        }
        m_Files.Clear();
        return wxDIR_CONTINUE;
      }

    private:
      const wxString& m_ExcludeDir;
      wxArrayString&  m_Files;
  };
}

int idTimerParsingOneByOne = wxNewId();
bool s_DebugSmartSense = false;

NativeParser::NativeParser() :
  m_TimerParsingOneByOne( this, idTimerParsingOneByOne ),
  m_ClassBrowser( nullptr ),
  m_ClassBrowserIsFloating( false ),
  m_ImageList( nullptr ),
  m_ParserPerWorkspace( false ),
  m_LastAISearchWasGlobal( false ),
  m_LastControl( nullptr ),
  m_LastFunctionIndex( -1 ),
  m_LastFuncTokenIdx( -1 ),
  m_LastLine( -1 ),
  m_LastResult( -1 ) {
  m_TempParser = new Parser( this, nullptr );
  m_Parser     = m_TempParser;
  m_ImageList = new wxImageList( 16, 16 );
  wxBitmap bmp;
  wxString prefix;
  prefix = ConfigManager::GetDataFolder() + _T( "/images/codecompletion/" );
  bmp = M_Code_LoadBitmap( prefix + _T( "class_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "class.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "class_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "class_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "class_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "ctor_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "ctor_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "ctor_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "dtor_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "dtor_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "dtor_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "method_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "method_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "method_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "var_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "var_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "var_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "macro_def.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "enum.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "enum_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "enum_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "enum_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "enumerator.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "namespace.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "typedef.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "typedef_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "typedef_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "typedef_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "symbols_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "vars_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "funcs_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "enums_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "macro_def_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "others_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "typedefs_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "macro_use.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "macro_use_private.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "macro_use_protected.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "macro_use_public.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  bmp = M_Code_LoadBitmap( prefix + _T( "macro_use_folder.png" ), wxBITMAP_TYPE_PNG );
  m_ImageList->Add( bmp );
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  m_ParserPerWorkspace = cfg->ReadBool( _T( "/parser_per_workspace" ), false );
  ProjectLoaderHooks::HookFunctorBase* myhook = new ProjectLoaderHooks::HookFunctor<NativeParser>( this, &NativeParser::OnProjectLoadingHook );
  m_HookId = ProjectLoaderHooks::RegisterHook( myhook );
  Connect( ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( NativeParser::OnParserStart ) );
  Connect( ParserCommon::idParserEnd,   wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( NativeParser::OnParserEnd ) );
  Connect( idTimerParsingOneByOne,      wxEVT_TIMER,                 wxTimerEventHandler( NativeParser::OnParsingOneByOneTimer ) );
}

NativeParser::~NativeParser() {
  Disconnect( ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( NativeParser::OnParserStart ) );
  Disconnect( ParserCommon::idParserEnd,   wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( NativeParser::OnParserEnd ) );
  Disconnect( idTimerParsingOneByOne,      wxEVT_TIMER,                 wxTimerEventHandler( NativeParser::OnParsingOneByOneTimer ) );
  ProjectLoaderHooks::UnregisterHook( m_HookId, true );
  RemoveClassBrowser();
  ClearParsers();
  Delete( m_ImageList );
  Delete( m_TempParser );
}

ParserBase* NativeParser::GetParserByProject( M_Code_Project* project ) {
  if( m_ParserPerWorkspace ) {
    std::set<M_Code_Project*>::iterator it = m_ParsedProjects.find( project );
    if( it != m_ParsedProjects.end() ) {
      return m_ParserList.begin()->second;
    }
  } else {
    for( ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it ) {
      if( it->first == project ) {
        return it->second;
      }
    }
  }
  return nullptr;
}

ParserBase* NativeParser::GetParserByFilename( const wxString& filename ) {
  M_Code_Project* project = GetProjectByFilename( filename );
  return GetParserByProject( project );
}

M_Code_Project* NativeParser::GetProjectByParser( ParserBase* parser ) {
  for( ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it ) {
    if( it->second == parser ) {
      return it->first;
    }
  }
  return NULL;
}

M_Code_Project* NativeParser::GetProjectByFilename( const wxString& filename ) {
  M_Code_Project* activeProject = Manager::Get()->GetProjectManager()->GetActiveProject();
  if( activeProject ) {
    ParserBase* parser = GetParserByProject( activeProject );
    if( ( parser
          && parser->IsFileParsed( filename ) )
        || activeProject->GetFileByFilename( filename, false, true ) ) {
      return activeProject;
    } else {
      ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
      for( size_t i = 0; i < projs->GetCount(); ++i ) {
        M_Code_Project* project = projs->Item( i );
        if( !project || project == activeProject ) {
          continue;
        }
        parser = GetParserByProject( project );
        if( ( parser
              && parser->IsFileParsed( filename ) )
            || project->GetFileByFilename( filename, false, true ) ) {
          return project;
        }
      }
    }
  }
  return nullptr;
}

M_Code_Project* NativeParser::GetProjectByEditor( M_Code_Editor* editor ) {
  if( !editor ) {
    return nullptr;
  }
  ProjectFile* pf = editor->GetProjectFile();
  if( pf && pf->GetParentProject() ) {
    return pf->GetParentProject();
  }
  return GetProjectByFilename( editor->GetFilename() );
}

M_Code_Project* NativeParser::GetCurrentProject() {
  M_Code_Editor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  M_Code_Project* project = GetProjectByEditor( editor );
  if( !project ) {
    project = Manager::Get()->GetProjectManager()->GetActiveProject();
  }
  return project;
}

bool NativeParser::Done() {
  bool done = true;
  for( ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it ) {
    if( !it->second->Done() ) {
      done = false;
      break;
    }
  }
  return done;
}

int NativeParser::GetTokenKindImage( const Token* token ) {
  if( !token ) {
    return PARSER_IMG_NONE;
  }
  switch( token->m_TokenKind ) {
    case tkMacroDef:
      return PARSER_IMG_MACRO_DEF;
    case tkEnum:
      switch( token->m_Scope ) {
        case tsPublic:
          return PARSER_IMG_ENUM_PUBLIC;
        case tsProtected:
          return PARSER_IMG_ENUM_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_ENUM_PRIVATE;
        case tsUndefined:
        default:
          return PARSER_IMG_ENUM;
      }
    case tkEnumerator:
      return PARSER_IMG_ENUMERATOR;
    case tkClass:
      switch( token->m_Scope ) {
        case tsPublic:
          return PARSER_IMG_CLASS_PUBLIC;
        case tsProtected:
          return PARSER_IMG_CLASS_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_CLASS_PRIVATE;
        case tsUndefined:
        default:
          return PARSER_IMG_CLASS;
      }
    case tkNamespace:
      return PARSER_IMG_NAMESPACE;
    case tkTypedef:
      switch( token->m_Scope ) {
        case tsPublic:
          return PARSER_IMG_TYPEDEF_PUBLIC;
        case tsProtected:
          return PARSER_IMG_TYPEDEF_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_TYPEDEF_PRIVATE;
        case tsUndefined:
        default:
          return PARSER_IMG_TYPEDEF;
      }
    case tkMacroUse:
      switch( token->m_Scope ) {
        case tsPublic:
          return PARSER_IMG_MACRO_USE_PUBLIC;
        case tsProtected:
          return PARSER_IMG_MACRO_USE_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_MACRO_USE_PRIVATE;
        case tsUndefined:
        default:
          return PARSER_IMG_MACRO_USE;
      }
    case tkConstructor:
      switch( token->m_Scope ) {
        case tsProtected:
          return PARSER_IMG_CTOR_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_CTOR_PRIVATE;
        case tsUndefined:
        case tsPublic:
        default:
          return PARSER_IMG_CTOR_PUBLIC;
      }
    case tkDestructor:
      switch( token->m_Scope ) {
        case tsProtected:
          return PARSER_IMG_DTOR_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_DTOR_PRIVATE;
        case tsUndefined:
        case tsPublic:
        default:
          return PARSER_IMG_DTOR_PUBLIC;
      }
    case tkFunction:
      switch( token->m_Scope ) {
        case tsProtected:
          return PARSER_IMG_FUNC_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_FUNC_PRIVATE;
        case tsUndefined:
        case tsPublic:
        default:
          return PARSER_IMG_FUNC_PUBLIC;
      }
    case tkVariable:
      switch( token->m_Scope ) {
        case tsProtected:
          return PARSER_IMG_VAR_PROTECTED;
        case tsPrivate:
          return PARSER_IMG_VAR_PRIVATE;
        case tsUndefined:
        case tsPublic:
        default:
          return PARSER_IMG_VAR_PUBLIC;
      }
    case tkAnyContainer:
    case tkAnyFunction:
    case tkUndefined:
    default:
      return PARSER_IMG_NONE;
  }
}

wxArrayString NativeParser::GetAllPathsByFilename( const wxString& filename ) {
  wxArrayString dirs;
  const wxFileName fn( filename );
  wxDir dir( fn.GetPath() );
  if( !dir.IsOpened() ) {
    return wxArrayString();
  }
  wxArrayString files;
  NativeParserHelper::ParserDirTraverser traverser( wxEmptyString, files );
  const wxString filespec = fn.HasExt() ? fn.GetName() + _T( ".*" ) : fn.GetName();
  dir.Traverse( traverser, filespec, wxDIR_FILES );
  if( files.GetCount() == 1 ) {
    M_Code_Project* project = IsParserPerWorkspace() ? GetCurrentProject()
                              : GetProjectByParser( m_Parser );
    if( project ) {
      const wxString prjPath = project->GetCommonTopLevelPath();
      wxString priorityPath;
      if( fn.HasExt() && ( fn.GetExt().StartsWith( _T( "h" ) ) || fn.GetExt().StartsWith( _T( "c" ) ) ) ) {
        wxFileName priFn( prjPath );
        priFn.AppendDir( fn.GetExt().StartsWith( _T( "h" ) ) ? _T( "sdk" ) : _T( "include" ) );
        if( priFn.DirExists() ) {
          priorityPath = priFn.GetFullPath();
          wxDir priorityDir( priorityPath );
          if( priorityDir.IsOpened() ) {
            wxArrayString priorityPathSub;
            NativeParserHelper::ParserDirTraverser traverser_2( wxEmptyString, priorityPathSub );
            priorityDir.Traverse( traverser_2, filespec, wxDIR_FILES | wxDIR_DIRS );
            if( priorityPathSub.GetCount() == 1 )
            { AddPaths( dirs, priorityPathSub[0], fn.HasExt() ); }
          }
        }
      }
      if( dirs.IsEmpty() ) {
        wxDir prjDir( prjPath );
        if( prjDir.IsOpened() ) {
          wxArrayString prjDirSub;
          NativeParserHelper::ParserDirTraverser traverser_2( priorityPath, prjDirSub );
          prjDir.Traverse( traverser_2, filespec, wxDIR_FILES | wxDIR_DIRS );
          if( prjDirSub.GetCount() == 1 ) {
            AddPaths( dirs, prjDirSub[0], fn.HasExt() );
          }
        }
      }
    }
  }
  if( !files.IsEmpty() ) {
    AddPaths( dirs, files[0], fn.HasExt() );
  }
  return dirs;
}

void NativeParser::AddPaths( wxArrayString& dirs, const wxString& path, bool hasExt ) {
  wxString s;
  if( hasExt ) {
    s = UnixFilename( path.BeforeLast( _T( '.' ) ) ) + _T( "." );
  } else {
    s = UnixFilename( path );
  }
  if( dirs.Index( s, false ) == wxNOT_FOUND ) {
    dirs.Add( s );
  }
}

ParserBase* NativeParser::CreateParser( M_Code_Project* project ) {
  if( GetParserByProject( project ) ) {
    return nullptr;
  }
  if( m_ParserPerWorkspace && !m_ParsedProjects.empty() ) {
    return m_ParserList.begin()->second;
  }
  ParserBase* parser = new Parser( this, project );
  if( !DoFullParsing( project, parser ) ) {
    delete parser;
    return nullptr;
  }
  if( m_Parser == m_TempParser ) {
    SetParser( parser );
  }
  if( m_ParserPerWorkspace ) {
    m_ParsedProjects.insert( project );
  }
  m_ParserList.push_back( std::make_pair( project, parser ) );
  RemoveObsoleteParsers();
  return parser;
}

bool NativeParser::DeleteParser( M_Code_Project* project ) {
  wxString prj = ( project ? project->GetTitle() : _T( "*NONE*" ) );
  ParserList::iterator it = m_ParserList.begin();
  if( !m_ParserPerWorkspace ) {
    for( ; it != m_ParserList.end(); ++it ) {
      if( it->first == project ) {
        break;
      }
    }
  }
  if( it == m_ParserList.end() ) {
    return false;
  }
  bool removeProjectFromParser = false;
  if( m_ParserPerWorkspace ) {
    removeProjectFromParser = RemoveProjectFromParser( project );
  }
  if( m_ParsedProjects.empty() ) {
    delete it->second;
    if( it->second == m_Parser ) {
      m_Parser = nullptr;
      SetParser( m_TempParser );
    }
    m_ParserList.erase( it );
    return true;
  }
  if( removeProjectFromParser ) {
    return true;
  }
  return false;
}

bool NativeParser::ReparseFile( M_Code_Project* project, const wxString& filename ) {
  if( ParserCommon::FileType( filename ) == ParserCommon::ftOther ) {
    return false;
  }
  ParserBase* parser = GetParserByProject( project );
  if( !parser ) {
    return false;
  }
  if( !parser->UpdateParsingProject( project ) ) {
    return false;
  }
  return parser->Reparse( filename );
}

bool NativeParser::AddFileToParser( M_Code_Project* project, const wxString& filename, ParserBase* parser ) {
  if( ParserCommon::FileType( filename ) == ParserCommon::ftOther ) {
    return false;
  }
  if( !parser ) {
    parser = GetParserByProject( project );
    if( !parser ) {
      return false;
    }
  }
  if( !parser->UpdateParsingProject( project ) ) {
    return false;
  }
  return parser->AddFile( filename, project );
}

bool NativeParser::RemoveFileFromParser( M_Code_Project* project, const wxString& filename ) {
  ParserBase* parser = GetParserByProject( project );
  if( !parser ) {
    return false;
  }
  return parser->RemoveFile( filename );
}

void NativeParser::RereadParserOptions() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  bool useSymbolBrowser = cfg->ReadBool( _T( "/use_symbols_browser" ), true );
  if( useSymbolBrowser ) {
    if( !m_ClassBrowser ) {
      CreateClassBrowser();
      UpdateClassBrowser();
    } else if( m_ClassBrowserIsFloating != cfg->ReadBool( _T( "/as_floating_window" ), false ) ) {
      RemoveClassBrowser();
      CreateClassBrowser();
      UpdateClassBrowser();
    }
  } else if( !useSymbolBrowser && m_ClassBrowser ) {
    RemoveClassBrowser();
  }
  const bool parserPerWorkspace = cfg->ReadBool( _T( "/parser_per_workspace" ), false );
  if( m_Parser == m_TempParser ) {
    m_ParserPerWorkspace = parserPerWorkspace;
    return;
  }
  RemoveObsoleteParsers();
  ParserOptions opts = m_Parser->Options();
  m_Parser->ReadOptions();
  bool reparse = false;
  M_Code_Project* project = GetCurrentProject();
  if( opts.followLocalIncludes  != m_Parser->Options().followLocalIncludes
      || opts.followGlobalIncludes != m_Parser->Options().followGlobalIncludes
      || opts.wantPreprocessor     != m_Parser->Options().wantPreprocessor
      || opts.parseComplexMacros   != m_Parser->Options().parseComplexMacros
      || opts.platformCheck        != m_Parser->Options().platformCheck
      || m_ParserPerWorkspace      != parserPerWorkspace ) {
    if( msging( _T( "You changed some class parser options. Do you want to "
                    "reparse your projects now, using the new options?" ),
                _T( "Reparse?" ), wxYES_NO | wxICON_QUESTION ) == wxID_YES ) {
      reparse = true;
    }
  }
  if( reparse ) {
    ClearParsers();
  }
  m_ParserPerWorkspace = parserPerWorkspace;
  if( reparse ) {
    CreateParser( project );
  }
}

void NativeParser::ReparseCurrentProject() {
  M_Code_Project* project = GetCurrentProject();
  if( project ) {
    DeleteParser( project );
    CreateParser( project );
  }
}

void NativeParser::ReparseSelectedProject() {
  wxTreeCtrl* tree = Manager::Get()->GetProjectManager()->GetUI().GetTree();
  if( !tree ) {
    return;
  }
  wxTreeItemId treeItem = Manager::Get()->GetProjectManager()->GetUI().GetTreeSelection();
  if( !treeItem.IsOk() ) {
    return;
  }
  const FileTreeData* data = static_cast<FileTreeData*>( tree->GetItemData( treeItem ) );
  if( !data ) {
    return;
  }
  if( data->GetKind() == FileTreeData::ftdkProject ) {
    M_Code_Project* project = data->GetProject();
    if( project ) {
      DeleteParser( project );
      CreateParser( project );
    }
  }
}

size_t NativeParser::MarkItemsByAI( ccSearchData* searchData, TokenIdxSet&  result, bool reallyUseAI,
                                    bool isPrefix, bool caseSensitive, int caretPos ) {
  result.clear();
  if( !m_Parser->Done() ) {
    wxString msg( _T( "The Parser is still parsing files." ) );
    msg += m_Parser->NotDoneReason();
    CCLogger::Get()->DebugLog( msg );
    return 0;
  }
  TokenTree* tree = m_Parser->GetTempTokenTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  tree->Clear();
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  RemoveLastFunctionChildren( m_Parser->GetTokenTree(), m_LastFuncTokenIdx );
  TokenIdxSet search_scope;
  ParseUsingNamespace( searchData, search_scope, caretPos );
  ParseFunctionArguments( searchData, caretPos );
  ParseLocalBlock( searchData, search_scope, caretPos );
  if( !reallyUseAI ) {
    tree = m_Parser->GetTokenTree();
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    for( size_t i = 0; i < tree->size(); ++i ) {
      result.insert( i );
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    return result.size();
  }
  return AI( result, searchData, wxEmptyString, isPrefix, caseSensitive, &search_scope, caretPos );
}

size_t NativeParser::MarkItemsByAI( TokenIdxSet& result, bool reallyUseAI, bool isPrefix, bool caseSensitive, int caretPos ) {
  M_Code_Editor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !editor ) {
    return 0;
  }
  ccSearchData searchData = { editor->GetControl(), editor->GetFilename() };
  if( !searchData.control ) {
    return 0;
  }
  return MarkItemsByAI( &searchData, result, reallyUseAI, isPrefix, caseSensitive, caretPos );
}

int NativeParser::GetCallTips( wxArrayString& items, int& typedCommas, M_Code_Editor* ed, int pos ) {
  items.Clear();
  typedCommas = 0;
  int commas = 0;
  if( !ed || !m_Parser->Done() ) {
    items.Add( _T( "Parsing at the moment..." ) );
    return wxSCI_INVALID_POSITION;
  }
  ccSearchData searchData = { ed->GetControl(), ed->GetFilename() };
  if( pos == wxNOT_FOUND ) {
    pos = searchData.control->GetCurrentPos();
  }
  int nest = 0;
  while( --pos > 0 ) {
    const int style = searchData.control->GetStyleAt( pos );
    if( searchData.control->IsString( style )
        || searchData.control->IsCharacter( style )
        || searchData.control->IsComment( style ) ) {
      continue;
    }
    const wxChar ch = searchData.control->GetCharAt( pos );
    if( ch == _T( ';' ) ) {
      return wxSCI_INVALID_POSITION;
    } else if( ch == _T( ',' ) ) {
      if( nest == 0 ) {
        ++commas;
      }
    } else if( ch == _T( ')' ) ) {
      --nest;
    } else if( ch == _T( '(' ) ) {
      ++nest;
      if( nest > 0 ) {
        break;
      }
    }
  }
  while( --pos > 0 ) {
    if( searchData.control->GetCharAt( pos ) <= _T( ' ' ) || searchData.control->IsComment( searchData.control->GetStyleAt( pos ) ) ) {
      continue;
    }
    break;
  }
  const int start = searchData.control->WordStartPosition( pos, true );
  const int end = searchData.control->WordEndPosition( pos, true );
  const wxString target = searchData.control->GetTextRange( start, end );
  if( target.IsEmpty() ) {
    return wxSCI_INVALID_POSITION;
  }
  TokenIdxSet result;
  MarkItemsByAI( result, true, false, true, end );
  ComputeCallTip( m_Parser->GetTokenTree(), result, items );
  typedCommas = commas;
  items.Sort();
  return end;
}

wxArrayString& NativeParser::GetProjectSearchDirs( M_Code_Project* project ) {
  ProjectSearchDirsMap::iterator it;
  it = m_ProjectSearchDirsMap.find( project );
  if( it == m_ProjectSearchDirsMap.end() ) {
    it = m_ProjectSearchDirsMap.insert( m_ProjectSearchDirsMap.end(), std::make_pair( project, wxArrayString() ) );
  }
  return it->second;
}

void NativeParser::CreateClassBrowser() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  if( m_ClassBrowser || !cfg->ReadBool( _T( "/use_symbols_browser" ), true ) ) {
    return;
  }
  m_ClassBrowserIsFloating = cfg->ReadBool( _T( "/as_floating_window" ), false );
  if( m_ClassBrowserIsFloating ) {
    m_ClassBrowser = new ClassBrowser( Manager::Get()->GetAppWindow(), this );
    CodeBlocksDockEvent evt( cbEVT_ADD_DOCK_WINDOW );
    evt.name = _T( "SymbolsBrowser" );
    evt.title = _T( "符号浏览器" );
    evt.pWindow = m_ClassBrowser;
    evt.dockSide = CodeBlocksDockEvent::dsRight;
    evt.desiredSize.Set( 200, 250 );
    evt.floatingSize.Set( 200, 250 );
    evt.minimumSize.Set( 150, 150 );
    evt.shown = true;
    evt.hideable = true;
    Manager::Get()->ProcessEvent( evt );
    m_ClassBrowser->UpdateSash();
  } else {
    m_ClassBrowser = new ClassBrowser( Manager::Get()->GetProjectManager()->GetUI().GetNotebook(), this );
    Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->AddPage( m_ClassBrowser, _T( "符号浏览" ) );
    m_ClassBrowser->UpdateSash();
  }
  m_ClassBrowser->SetParser( m_Parser );
}

void NativeParser::RemoveClassBrowser( M_Code_unused bool appShutDown ) {
  if( !m_ClassBrowser ) {
    return;
  }
  if( m_ClassBrowserIsFloating ) {
    CodeBlocksDockEvent evt( cbEVT_REMOVE_DOCK_WINDOW );
    evt.pWindow = m_ClassBrowser;
    Manager::Get()->ProcessEvent( evt );
  } else {
    int idx = Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->GetPageIndex( m_ClassBrowser );
    if( idx != -1 ) {
      Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->RemovePage( idx );
    }
  }
  m_ClassBrowser->Destroy();
  m_ClassBrowser = NULL;
}

void NativeParser::UpdateClassBrowser() {
  if( !m_ClassBrowser ) {
    return;
  }
  if( m_Parser != m_TempParser && m_Parser->Done() && !Manager::IsAppShuttingDown() ) {
    m_ClassBrowser->UpdateClassBrowserView();
  }
}

bool NativeParser::DoFullParsing( M_Code_Project* project, ParserBase* parser ) {
  if( !parser ) {
    return false;
  }
  if( !AddCompilerDirs( project, parser ) ) {
    CCLogger::Get()->DebugLog( _T( "NativeParser::DoFullParsing(): AddCompilerDirs 失败!" ) );
  }
  if( !AddCompilerPredefinedMacros( project, parser ) ) {
    CCLogger::Get()->DebugLog( _T( "NativeParser::DoFullParsing(): AddCompilerPredefinedMacros 失败!" ) );
  }
  if( !AddProjectDefinedMacros( project, parser ) ) {
    CCLogger::Get()->DebugLog( _T( "NativeParser::DoFullParsing(): AddProjectDefinedMacros 失败!" ) );
  }
  if( project ) {
    if( !parser->Options().platformCheck || ( parser->Options().platformCheck && true ) ) {
      AddIncludeDirsToParser( GetProjectSearchDirs( project ), project->GetBasePath(), parser );
    }
  }
  StringList localSources;
  if( project ) {
    for( FilesList::const_iterator fl_it = project->GetFilesList().begin();
         fl_it != project->GetFilesList().end(); ++fl_it ) {
      ProjectFile* pf = *fl_it;
      if( !pf ) {
        continue;
      }
      ParserCommon::EFileType ft = ParserCommon::FileType( pf->relativeFilename );
      if( ft == ParserCommon::ftSource ) {
        localSources.push_back( pf->file.GetFullPath() );
      }
    }
  }
  wxString prj = ( project ? project->GetTitle() : _T( "*NONE*" ) );
  if( !localSources.empty() ) {
    parser->AddBatchParse( localSources );
  }
  return true;
}

bool NativeParser::SwitchParser( M_Code_Project* project, ParserBase* parser ) {
  if( !parser || parser == m_Parser || GetParserByProject( project ) != parser ) {
    return false;
  }
  SetParser( parser );
  return true;
}

void NativeParser::SetParser( ParserBase* parser ) {
  if( m_Parser == parser ) {
    return;
  }
  if( m_Parser ) {
    RemoveLastFunctionChildren( m_Parser->GetTokenTree(), m_LastFuncTokenIdx );
  }
  InitCCSearchVariables();
  m_Parser = parser;
  if( m_ClassBrowser ) {
    m_ClassBrowser->SetParser( parser );
  }
}

void NativeParser::ClearParsers() {
  if( m_ParserPerWorkspace ) {
    while( !m_ParsedProjects.empty() && DeleteParser( *m_ParsedProjects.begin() ) ) ;
  } else {
    while( !m_ParserList.empty() && DeleteParser( m_ParserList.begin()->first ) ) ;
  }
}

void NativeParser::RemoveObsoleteParsers() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  const size_t maxParsers = cfg->ReadInt( _T( "/max_parsers" ), 5 );
  wxArrayString removedProjectNames;
  std::pair<M_Code_Project*, ParserBase*> info = GetParserInfoByCurrentEditor();
  while( m_ParserList.size() > maxParsers ) {
    bool deleted = false;
    for( ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it ) {
      if( it->second == info.second ) {
        continue;
      }
      wxString prj = ( it->first ? it->first->GetTitle() : _T( "*NONE*" ) );
      if( DeleteParser( it->first ) ) {
        removedProjectNames.Add( prj );
        deleted = true;
        break;
      }
    }
    if( !deleted ) {
      break;
    }
  }
}

std::pair<M_Code_Project*, ParserBase*> NativeParser::GetParserInfoByCurrentEditor() {
  std::pair<M_Code_Project*, ParserBase*> info( nullptr, nullptr );
  M_Code_Editor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( editor ) {
    info.first  = GetProjectByEditor( editor );
    info.second = GetParserByProject( info.first );
  }
  return info;
}

void NativeParser::SetTokenKindImage( int ( kind ), const wxBitmap& ( bitmap ), const wxBitmap& ( mask ) ) {
  return;
}

void NativeParser::SetTokenKindImage( int kind, const wxBitmap& bitmap, M_Code_unused const wxColour& maskColour ) {
  if( kind < PARSER_IMG_MIN || kind > PARSER_IMG_MAX ) {
    return;
  }
  m_ImageList->Replace( kind, bitmap );
}

void NativeParser::SetTokenKindImage( int kind, const wxIcon& icon ) {
  if( kind < PARSER_IMG_MIN || kind > PARSER_IMG_MAX ) {
    return;
  }
  m_ImageList->Replace( kind, icon );
}

void NativeParser::SetCBViewMode( const BrowserViewMode& mode ) {
  m_Parser->ClassBrowserOptions().showInheritance = ( mode == bvmInheritance ) ? true : false;
  UpdateClassBrowser();
}

void NativeParser::OnProjectLoadingHook( M_Code_Project* project, TiXmlElement* elem, bool loading ) {
  if( loading ) {
    wxArrayString& pdirs = GetProjectSearchDirs( project );
    TiXmlElement* CCConf = elem->FirstChildElement( "code_completion" );
    if( CCConf ) {
      TiXmlElement* pathsElem = CCConf->FirstChildElement( "search_path" );
      while( pathsElem ) {
        if( pathsElem->Attribute( "add" ) ) {
          wxString dir = cbC2U( pathsElem->Attribute( "add" ) );
          if( pdirs.Index( dir ) == wxNOT_FOUND ) {
            pdirs.Add( dir );
          }
        }
        pathsElem = pathsElem->NextSiblingElement( "search_path" );
      }
    }
  } else {
    wxArrayString& pdirs = GetProjectSearchDirs( project );
    TiXmlElement* node = elem->FirstChildElement( "code_completion" );
    if( !node ) {
      node = elem->InsertEndChild( TiXmlElement( "code_completion" ) )->ToElement();
    }
    if( node ) {
      node->Clear();
      for( size_t i = 0; i < pdirs.GetCount(); ++i ) {
        TiXmlElement* path = node->InsertEndChild( TiXmlElement( "search_path" ) )->ToElement();
        if( path ) {
          path->SetAttribute( "add", cbU2C( pdirs[i] ) );
        }
      }
    }
  }
}

size_t NativeParser::AI( TokenIdxSet& result, ccSearchData* searchData, const wxString& lineText,
                         bool isPrefix, bool caseSensitive, TokenIdxSet* search_scope, int caretPos ) {
  m_LastAISearchWasGlobal = false;
  m_LastAIGlobalSearch.Clear();
  int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
  if( pos < 0 || pos > searchData->control->GetLength() ) {
    return 0;
  }
  int line = searchData->control->LineFromPosition( pos );
  wxString actual_search( lineText );
  if( actual_search.IsEmpty() ) {
    const int startPos = searchData->control->PositionFromLine( line );
    actual_search = searchData->control->GetTextRange( startPos, pos ).Trim();
  }
  TokenTree* tree = m_Parser->GetTokenTree();
  TokenIdxSet proc_result;
  size_t found_at = FindCurrentFunctionToken( searchData, proc_result, pos );
  TokenIdxSet scope_result;
  if( found_at ) {
    FindCurrentFunctionScope( tree, proc_result, scope_result );
  }
  if( !search_scope ) {
    search_scope = &scope_result;
  } else {
    for( TokenIdxSet::const_iterator tis_it = scope_result.begin(); tis_it != scope_result.end(); ++tis_it ) {
      search_scope->insert( *tis_it );
    }
  }
  CleanupSearchScope( tree, search_scope );
  std::queue<ParserComponent> components;
  BreakUpComponents( actual_search, components );
  m_LastAISearchWasGlobal = components.size() <= 1;
  if( !components.empty() ) {
    m_LastAIGlobalSearch = components.front().component;
  }
  ResolveExpression( tree, components, *search_scope, result, caseSensitive, isPrefix );
  return result.size();
}


size_t NativeParser::FindCurrentFunctionToken( ccSearchData* searchData, TokenIdxSet& result, int caretPos ) {
  TokenIdxSet scope_result;
  wxString procName;
  wxString scopeName;
  FindCurrentFunctionStart( searchData, &scopeName, &procName, nullptr, caretPos );
  if( procName.IsEmpty() ) {
    return 0;
  }
  if( !scopeName.IsEmpty() ) {
    scopeName.RemoveLast();
    scopeName.RemoveLast();
    std::queue<ParserComponent> ns;
    BreakUpComponents( scopeName, ns );
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    FindAIMatches( m_Parser->GetTokenTree(), ns, scope_result, -1,
                   true, true, false, tkNamespace | tkClass | tkTypedef );
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  }
  if( scope_result.empty() ) {
    scope_result.insert( -1 );
  }
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  for( TokenIdxSet::const_iterator tis_it = scope_result.begin(); tis_it != scope_result.end(); ++tis_it ) {
    GenerateResultSet( m_Parser->GetTokenTree(), procName, *tis_it, result,
                       true, false, tkAnyFunction | tkClass );
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return result.size();
}

int NativeParser::FindCurrentFunctionStart( ccSearchData* searchData,
    wxString* nameSpace,
    wxString* procName,
    int* functionIndex,
    int caretPos ) {
  int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
  if( ( pos < 0 ) || ( pos > searchData->control->GetLength() ) ) {
    if( s_DebugSmartSense ) {
      return -1;
    }
  }
  const int curLine = searchData->control->LineFromPosition( pos ) + 1;
  if( ( curLine == m_LastLine ) && ( ( searchData->control == m_LastControl ) && ( !searchData->control->GetModify() ) )
      && ( searchData->file == m_LastFile ) ) {
    if( nameSpace ) {
      *nameSpace     = m_LastNamespace;
    }
    if( procName ) {
      *procName = m_LastPROC;
    }
    if( functionIndex ) {
      *functionIndex = m_LastFunctionIndex;
    }
    return m_LastResult;
  }
  m_LastFile    = searchData->file;
  m_LastControl = searchData->control;
  m_LastLine    = curLine;
  TokenIdxSet result;
  TokenTree* tree = m_Parser->GetTokenTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  const int idx = GetTokenFromCurrentLine( tree, result, curLine, searchData->file );
  const Token* token = tree->at( idx );
  if( token ) {
    if( s_DebugSmartSense ) {
      m_LastNamespace = token->GetNamespace();
    }
    m_LastPROC = token->m_Name;
    m_LastFunctionIndex  = token->m_Index;
    m_LastResult = searchData->control->PositionFromLine( token->m_ImplLine - 1 );
    if( token->m_TokenKind & tkAnyFunction ) {
      while( m_LastResult < searchData->control->GetTextLength() ) {
        wxChar ch = searchData->control->GetCharAt( m_LastResult );
        if( ch == _T( '{' ) ) {
          break;
        } else if( ch == 0 ) {
          CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
          return -1;
        }
        ++m_LastResult;
      }
    }
    if( nameSpace ) {
      *nameSpace = m_LastNamespace;
    }
    if( procName ) {
      *procName = m_LastPROC;
    }
    if( functionIndex ) {
      *functionIndex = token->m_Index;
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    return m_LastResult;
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  m_LastResult = -1;
  return -1;
}

bool NativeParser::ParseUsingNamespace( ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos ) {
  int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
  if( pos < 0 || pos > searchData->control->GetLength() ) {
    return false;
  }
  wxString buffer = searchData->control->GetTextRange( 0, pos );
  return ParseBufferForUsingNamespace( buffer, search_scope );
}

bool NativeParser::ParseBufferForUsingNamespace( const wxString& buffer, TokenIdxSet& search_scope, bool bufferSkipBlocks ) {
  wxArrayString ns;
  m_Parser->ParseBufferForUsingNamespace( buffer, ns, bufferSkipBlocks );
  TokenTree* tree = m_Parser->GetTokenTree();
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  for( size_t i = 0; i < ns.GetCount(); ++i ) {
    std::queue<ParserComponent> components;
    BreakUpComponents( ns[i], components );
    int parentIdx = -1;
    while( !components.empty() ) {
      ParserComponent pc = components.front();
      components.pop();
      int id = tree->TokenExists( pc.component, parentIdx, tkNamespace );
      if( id == -1 ) {
        parentIdx = -1;
        break;
      }
      parentIdx = id;
    }
    search_scope.insert( parentIdx );
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return true;
}

bool NativeParser::ParseFunctionArguments( ccSearchData* searchData, int caretPos ) {
  TokenIdxSet proc_result;
  TokenTree* tree = m_Parser->GetTokenTree();
  size_t found_at = FindCurrentFunctionToken( searchData, proc_result, caretPos );
  if( !found_at ) {
    return false;
  }
  const int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
  const unsigned int curLine = searchData->control->LineFromPosition( pos ) + 1;
  bool locked = false;
  for( TokenIdxSet::const_iterator tis_it = proc_result.begin(); tis_it != proc_result.end(); ++tis_it ) {
    wxString buffer;
    int initLine = -1;
    if( locked )
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
      locked = true;
    const Token* token = tree->at( *tis_it );
    if( !token ) {
      continue;
    }
    if( curLine < token->m_ImplLineStart || curLine > token->m_ImplLineEnd ) {
      continue;
    }
    if( !token->m_Args.IsEmpty() && !token->m_Args.Matches( _T( "()" ) ) ) {
      buffer = token->m_Args;
      buffer.Remove( 0, 1 );
      buffer.RemoveLast();
      buffer.Replace( _T( "," ), _T( ";" ) );
      buffer << _T( ';' );
      buffer.Trim();
      if( !buffer.IsEmpty() ) {
        const int textLength = searchData->control->GetLength();
        if( textLength == -1 ) {
          continue;
        }
        int paraPos = searchData->control->PositionFromLine( token->m_ImplLine - 1 );
        if( paraPos == -1 ) {
          continue;
        }
        while( paraPos < textLength && searchData->control->GetCharAt( paraPos++ ) != _T( '(' ) )
          ;
        while( paraPos < textLength && searchData->control->GetCharAt( paraPos++ ) < _T( ' ' ) )
          ;
        initLine = searchData->control->LineFromPosition( paraPos ) + 1;
        if( initLine == -1 ) {
          continue;
        }
      }
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    locked = false;
  }
  if( locked )
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    return true;
}

bool NativeParser::ParseLocalBlock( ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos ) {
  int parentIdx = -1;
  int blockStart = FindCurrentFunctionStart( searchData, nullptr, nullptr, &parentIdx, caretPos );
  int initLine = 0;
  if( parentIdx != -1 ) {
    TokenTree* tree = m_Parser->GetTokenTree();
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    const Token* parent = tree->at( parentIdx );
    if( parent && ( parent->m_TokenKind & tkAnyFunction ) ) {
      m_LastFuncTokenIdx = parent->m_Index;
      initLine = parent->m_ImplLineStart;
    }
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    if( !parent || !( parent->m_TokenKind & tkAnyFunction ) ) {
      return false;
    }
  }
  if( blockStart != -1 ) {
    M_Code_StyledTextCtrl* stc = searchData->control;
    if( stc->GetCharAt( blockStart ) == _T( '{' ) ) {
      ++blockStart;
    }
    const int pos = ( caretPos == -1 ? stc->GetCurrentPos() : caretPos );
    const int line = stc->LineFromPosition( pos );
    const int blockEnd = stc->GetLineEndPosition( line );
    if( blockEnd < 0 || blockEnd > stc->GetLength() ) {
      return false;
    }
    if( blockStart >= blockEnd ) {
      blockStart = blockEnd;
    }
    wxString buffer;
    int scanPos = blockEnd;
    for( int curPos = pos; curPos > blockStart; --curPos ) {
      if( stc->GetCharAt( curPos ) != _T( '}' ) ) {
        continue;
      }
      const int style = stc->GetStyleAt( curPos );
      if( stc->IsString( style ) || stc->IsCharacter( style ) || stc->IsComment( style ) ) {
        continue;
      }
      const int scopeStart = stc->BraceMatch( curPos );
      if( scopeStart < blockStart ) {
        break;
      }
      buffer.Prepend( stc->GetTextRange( curPos, scanPos ) );
      int startLn = stc->LineFromPosition( scopeStart );
      int endLn   = stc->LineFromPosition( curPos );
      if( startLn < endLn ) {
        buffer.Prepend( wxString( _T( '\n' ), endLn - startLn ) );
      }
      scanPos = scopeStart + 1;
      curPos  = scopeStart;
      int prevCharIdx = scopeStart - 1;
      for( ; prevCharIdx > blockStart; --prevCharIdx ) {
        if( stc->IsComment( stc->GetStyleAt( prevCharIdx ) ) ) {
          continue;
        }
        if( !wxIsspace( stc->GetCharAt( prevCharIdx ) ) ) {
          break;
        }
      }
      if( stc->GetCharAt( prevCharIdx ) != _T( ')' ) ) {
        continue;
      }
      const int paramStart = stc->BraceMatch( prevCharIdx );
      if( paramStart < blockStart ) {
        continue;
      }
      for( prevCharIdx = paramStart - 1; prevCharIdx > blockStart; --prevCharIdx ) {
        if( stc->IsComment( stc->GetStyleAt( prevCharIdx ) ) ) {
          continue;
        }
        if( !wxIsspace( stc->GetCharAt( prevCharIdx ) ) ) {
          break;
        }
      }
      const wxString text = stc->GetTextRange( stc->WordStartPosition( prevCharIdx, true ), stc->WordEndPosition( prevCharIdx, true ) );
      if( text == _T( "for" ) ) {
        buffer.Prepend( _T( "(;;){" ) );
      } else if( text == _T( "if" ) || text == _T( "while" ) || text == _T( "catch" ) ) {
        buffer.Prepend( _T( "(0){" ) );
      } else {
        continue;
      }
      startLn = stc->LineFromPosition( prevCharIdx );
      endLn = stc->LineFromPosition( scopeStart );
      if( startLn < endLn ) {
        buffer.Prepend( wxString( _T( '\n' ), endLn - startLn ) );
      }
      curPos  = stc->WordStartPosition( prevCharIdx, true );
      scanPos = stc->WordEndPosition( prevCharIdx, true );
    }
    buffer.Prepend( stc->GetTextRange( blockStart, scanPos ) );
    buffer.Trim();
    ParseBufferForUsingNamespace( buffer, search_scope, false );
    if( !buffer.IsEmpty() && !m_Parser->ParseBuffer( buffer, false, false, true, searchData->file, m_LastFuncTokenIdx, initLine ) ) {
      ;
    } else {
      if( s_DebugSmartSense ) {
        TokenTree* tree = m_Parser->GetTokenTree();
        CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
        for( size_t i = 0; i < tree->size(); ++i ) {
          const Token* token = tree->at( i );
          if( token && token->m_IsTemp ) {
            wxString log( F( _T( " + %s (%d)" ), token->DisplayName().wx_str(), token->m_Index ) );
            const Token* parent = tree->at( token->m_ParentIndex );
            if( parent ) {
              log += F( _T( "; Parent = %s (%d)" ), parent->m_Name.wx_str(), token->m_ParentIndex );
            }
            CCLogger::Get()->DebugLog( log );
          }
        }
        CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      }
      return true;
    }
  }
  return false;
}

bool NativeParser::AddCompilerDirs( M_Code_Project* project, ParserBase* parser ) {
  if( !parser ) {
    return false;
  }
  if( !project ) {
    AddCompilerIncludeDirsToParser( CompilerFactory::GetDefaultCompiler(), parser );
    return true;
  }
  wxString base = project->GetBasePath();
  parser->AddIncludeDir( base );
  Compiler* compiler = CompilerFactory::GetCompiler( project->GetCompilerID() );
  cb::shared_ptr<CompilerCommandGenerator> generator( compiler ? compiler->GetCommandGenerator( project ) : nullptr );
  if( !parser->Options().platformCheck || ( parser->Options().platformCheck && true ) ) {
    AddIncludeDirsToParser( project->GetIncludeDirs(), base, parser );
  }
  int nCompilers = 1 + project->GetBuildTargetsCount();
  Compiler** Compilers = new Compiler* [nCompilers];
  memset( Compilers, 0, sizeof( Compiler* ) * nCompilers );
  nCompilers = 0;
  for( int i = 0; i < project->GetBuildTargetsCount(); ++i ) {
    ProjectBuildTarget* target = project->GetBuildTarget( i );
    if( !target ) {
      continue;
    }
    if( !parser->Options().platformCheck || ( parser->Options().platformCheck && true ) ) {
      if( compiler && generator ) {
        AddIncludeDirsToParser( generator->GetCompilerSearchDirs( target ), base, parser );
      }
      AddIncludeDirsToParser( target->GetIncludeDirs(), base, parser );
      wxString CompilerIndex = target->GetCompilerID();
      Compiler* tgtCompiler = CompilerFactory::GetCompiler( CompilerIndex );
      if( tgtCompiler ) {
        Compilers[nCompilers] = tgtCompiler;
        ++nCompilers;
      }
    }
  }
  if( compiler ) {
    Compilers[nCompilers++] = compiler;
  }
  for( int idxCompiler = 0; idxCompiler < nCompilers; ++idxCompiler ) {
    AddCompilerIncludeDirsToParser( Compilers[idxCompiler], parser );
  }
  delete [] Compilers;
  return true;
}

bool NativeParser::AddCompilerPredefinedMacros( M_Code_Project* project, ParserBase* parser ) {
  if( !parser ) {
    return false;
  }
  if( !parser->Options().wantPreprocessor ) {
    return false;
  }
  wxString compilerId = project ? project->GetCompilerID() : CompilerFactory::GetDefaultCompilerID();
  wxString defs;
  if( compilerId.Contains( _T( "gcc" ) ) ) {
    if( !AddCompilerPredefinedMacrosGCC( compilerId, project, defs, parser ) ) {
      return false;
    }
  } else if( compilerId.StartsWith( _T( "msvc" ) ) ) {
    if( !AddCompilerPredefinedMacrosVC( compilerId, defs, parser ) ) {
      return false;
    }
  }
  parser->AddPredefinedMacros( defs );
  if( defs.IsEmpty() ) {
    return false;
  }
  return true;
}

bool NativeParser::AddCompilerPredefinedMacrosGCC( const wxString& compilerId, M_Code_Project* project, wxString& defs, ParserBase* parser ) {
  Compiler* compiler = CompilerFactory::GetCompiler( compilerId );
  if( !compiler ) {
    return false;
  }
  if( parser->Options().platformCheck && false ) {
    return false;
  }
  wxString masterPath = compiler->GetMasterPath();
  Manager::Get()->GetMacrosManager()->ReplaceMacros( masterPath );
  const wxString cpp_compiler = masterPath + _T( "/bin/" ) + compiler->GetPrograms().CPP;
  if( !wxFileName::FileExists( cpp_compiler ) ) {
    return false;
  }
  static std::map<wxString, wxString> gccDefsMap;
  if( gccDefsMap[cpp_compiler].IsEmpty() ) {
    static bool reentry = false;
    if( reentry ) {
      return false;
    }
    wxString standard = GetCompilerStandardGCC( compiler, project );
    const wxString args( F( _T( " -E -dM -x c++ %s /dev/null" ), standard.wx_str() ) );
    wxArrayString output;
    reentry = true;
    if( wxExecute( cpp_compiler + args, output, wxEXEC_SYNC | wxEXEC_NODISABLE ) == -1 ) {
      reentry = false;
      return false;
    }
    reentry = false;
    if( Manager::IsAppShuttingDown() ) {
      return false;
    }
    wxString& gccDefs = gccDefsMap[cpp_compiler];
    for( size_t i = 0; i < output.Count(); ++i ) {
      gccDefs += output[i] + _T( "\n" );
    }
  }
  defs = gccDefsMap[cpp_compiler];
  return true;
}

wxString NativeParser::GetCompilerStandardGCC( Compiler* compiler, M_Code_Project* project ) {
  wxString standard = GetCompilerUsingStandardGCC( compiler->GetCompilerOptions() );
  if( standard.IsEmpty() && project ) {
    standard = GetCompilerUsingStandardGCC( project->GetCompilerOptions() );
    if( standard.IsEmpty() ) {
      for( int i = 0; i < project->GetBuildTargetsCount(); ++i ) {
        ProjectBuildTarget* target = project->GetBuildTarget( i );
        standard = GetCompilerUsingStandardGCC( target->GetCompilerOptions() );
        if( !standard.IsEmpty() ) {
          break;
        }
      }
    }
  }
  return standard;
}

wxString NativeParser::GetCompilerUsingStandardGCC( const wxArrayString& compilerOptions ) {
  wxString standard;
  for( wxArrayString::size_type i = 0; i < compilerOptions.Count(); ++i ) {
    if( compilerOptions[i].StartsWith( _T( "-std=" ) ) ) {
      standard = compilerOptions[i];
      break;
    }
  }
  return standard;
}

bool NativeParser::AddCompilerPredefinedMacrosVC( const wxString& compilerId, wxString& defs, ParserBase* parser ) {
  static wxString vcDefs;
  static bool firstExecute = true;
  if( !firstExecute ) {
    defs = vcDefs;
    return true;
  }
  firstExecute = false;
  Compiler* compiler = CompilerFactory::GetCompiler( compilerId );
  if( !compiler ) {
    return false;
  }
  if( parser->Options().platformCheck && false ) {
    return false;
  }
  wxString masterPath = compiler->GetMasterPath();
  Manager::Get()->GetMacrosManager()->ReplaceMacros( masterPath );
  const wxString c_compiler = masterPath + _T( "/bin/" ) + compiler->GetPrograms().C;
  if( !wxFileName::FileExists( c_compiler ) ) {
    return false;
  }
  static bool reentry = false;
  if( reentry ) {
    return false;
  }
  wxArrayString output, error;
  reentry = true;
  if( wxExecute( c_compiler, output, error, wxEXEC_SYNC | wxEXEC_NODISABLE ) == -1 ) {
    reentry = false;
    return false;
  }
  reentry = false;
  if( Manager::IsAppShuttingDown() ) {
    return false;
  }
  if( error.IsEmpty() ) {
    return false;
  }
  wxString compilerVersionInfo = error[0];
  wxString tmp( _T( "Microsoft (R) " ) );
  int pos = compilerVersionInfo.Find( tmp );
  if( pos != wxNOT_FOUND ) {
    wxString bit = compilerVersionInfo.Mid( pos + tmp.Length(), 2 );
    if( ( bit.IsSameAs( _T( "32" ) ) ) || compilerVersionInfo.Contains( _T( "x86" ) ) ) {
      defs += _T( "#define _WIN32" ) _T( "\n" );
    } else if( ( bit.IsSameAs( _T( "64" ) ) ) || compilerVersionInfo.Contains( _T( "x64" ) ) ) {
      defs += _T( "#define _WIN64" ) _T( "\n" );
    }
  }
  tmp = _T( "Compiler Version " );
  pos = compilerVersionInfo.Find( tmp );
  if( pos != wxNOT_FOUND ) {
    wxString ver = compilerVersionInfo.Mid( pos + tmp.Length(), 4 );
    pos = ver.Find( _T( '.' ) );
    if( pos != wxNOT_FOUND ) {
      ver[pos]     = ver[pos + 1];
      ver[pos + 1] = _T( '0' );
      defs += _T( "#define _MSC_VER " ) + ver;
    }
  }
  defs = vcDefs;
  return true;
}

bool NativeParser::AddProjectDefinedMacros( M_Code_Project* project, ParserBase* parser ) {
  if( !parser ) {
    return false;
  }
  if( !project ) {
    return true;
  }
  wxString compilerId = project->GetCompilerID();
  wxString defineCompilerSwitch( wxEmptyString );
  if( compilerId.Contains( _T( "gcc" ) ) ) {
    defineCompilerSwitch = _T( "-D" );
  } else if( compilerId.StartsWith( _T( "msvc" ) ) ) {
    defineCompilerSwitch = _T( "/D" );
  }
  if( defineCompilerSwitch.IsEmpty() ) {
    return false;
  }
  wxString defs;
  wxArrayString opts;
  if( !parser->Options().platformCheck
      || ( parser->Options().platformCheck && true ) ) {
    opts = project->GetCompilerOptions();
  }
  ProjectBuildTarget* target = project->GetBuildTarget( project->GetActiveBuildTarget() );
  if( target != NULL ) {
    if( !parser->Options().platformCheck
        || ( parser->Options().platformCheck && true ) ) {
      wxArrayString targetOpts = target->GetCompilerOptions();
      for( size_t i = 0; i < targetOpts.GetCount(); ++i ) {
        opts.Add( targetOpts[i] );
      }
    }
  }
  wxArrayString targets = project->GetExpandedVirtualBuildTargetGroup( project->GetActiveBuildTarget() );
  for( size_t i = 0; i < targets.GetCount(); ++i ) {
    target = project->GetBuildTarget( targets[i] );
    if( target != NULL ) {
      if( !parser->Options().platformCheck
          || ( parser->Options().platformCheck && true ) ) {
        wxArrayString targetOpts = target->GetCompilerOptions();
        for( size_t j = 0; j < targetOpts.GetCount(); ++j ) {
          opts.Add( targetOpts[j] );
        }
      }
    }
  }
  for( size_t i = 0; i < opts.GetCount(); ++i ) {
    wxString def = opts[i];
    Manager::Get()->GetMacrosManager()->ReplaceMacros( def );
    if( !def.StartsWith( defineCompilerSwitch ) ) {
      continue;
    }
    def = def.Right( def.Length() - defineCompilerSwitch.Length() );
    int pos = def.Find( _T( '=' ) );
    if( pos != wxNOT_FOUND ) {
      def[pos] = _T( ' ' );
    }
    defs += _T( "#define " ) + def + _T( "\n" );
  }
  parser->AddPredefinedMacros( defs );
  if( defs.IsEmpty() ) {
    return false;
  }
  return true;
}

void NativeParser::AddCompilerIncludeDirsToParser( const Compiler* compiler, ParserBase* parser ) {
  if( !compiler || !parser ) {
    return;
  }
  if( !parser->Options().platformCheck
      || ( parser->Options().platformCheck && true ) ) {
    AddIncludeDirsToParser( compiler->GetIncludeDirs(), wxEmptyString, parser );
    if( compiler->GetID().Contains( _T( "gcc" ) ) ) {
      AddGCCCompilerDirs( compiler->GetMasterPath(), compiler->GetPrograms().CPP, parser );
    }
  }
}

const wxArrayString& NativeParser::GetGCCCompilerDirs( const wxString &cpp_compiler ) {
  static std::map<wxString, wxArrayString> dirs;
  static wxArrayString cached_result;
  cached_result = dirs[cpp_compiler];
  if( !cached_result.IsEmpty() ) {
    return cached_result;
  }
  if( !wxFileExists( cpp_compiler ) ) {
    return cached_result;
  }
  if( Manager::IsAppShuttingDown() ) {
    return cached_result;
  }
  wxString Command = ( cpp_compiler + _T( " -v -E -x c++ /dev/null" ) );
  static bool reentry_protection = false;
  if( reentry_protection ) {
    return cached_result;
  }
  reentry_protection = true;
  wxArrayString Output, Errors;
  if( wxExecute( Command, Output, Errors, wxEXEC_SYNC | wxEXEC_NODISABLE ) == -1 ) {
    reentry_protection = false;
    return cached_result;
  }
  reentry_protection = false;
  if( Manager::IsAppShuttingDown() ) {
    return cached_result;
  }
  bool start = false;
  for( size_t idxCount = 0; idxCount < Errors.GetCount(); ++idxCount ) {
    wxString path = Errors[idxCount].Trim( true ).Trim( false );
    if( !start ) {
      if( !path.StartsWith( _T( "#include <...>" ) ) ) {
        continue;
      }
      path = Errors[++idxCount].Trim( true ).Trim( false );
      start = true;
    }
    wxFileName fname( path, wxEmptyString );
    fname.Normalize();
    fname.SetVolume( fname.GetVolume().MakeUpper() );
    if( !fname.DirExists() ) {
      break;
    }
    dirs[cpp_compiler].Add( fname.GetPath() );
  }
  return dirs[cpp_compiler];
}

void NativeParser::AddGCCCompilerDirs( const wxString& masterPath, const wxString& compilerCpp, ParserBase* parser ) {
  wxFileName fn( wxEmptyString, compilerCpp );
  wxString masterPathNoMacros( masterPath );
  Manager::Get()->GetMacrosManager()->ReplaceMacros( masterPathNoMacros );
  fn.SetPath( masterPathNoMacros );
  fn.AppendDir( _T( "bin" ) );
  const wxArrayString& gccDirs = GetGCCCompilerDirs( fn.GetFullPath() );
  for( size_t i = 0; i < gccDirs.GetCount(); ++i ) {
    parser->AddIncludeDir( gccDirs[i] );
  }
}

void NativeParser::AddIncludeDirsToParser( const wxArrayString& dirs, const wxString& base, ParserBase* parser ) {
  for( unsigned int i = 0; i < dirs.GetCount(); ++i ) {
    wxString dir = dirs[i];
    Manager::Get()->GetMacrosManager()->ReplaceMacros( dir );
    if( !base.IsEmpty() ) {
      wxFileName fn( dir );
      if( NormalizePath( fn, base ) ) {
        parser->AddIncludeDir( fn.GetFullPath() );
      }
    } else
    { parser->AddIncludeDir( dir ); }
  }
}

void NativeParser::OnParserStart( wxCommandEvent& event ) {
  M_Code_Project* project = static_cast<M_Code_Project*>( event.GetClientData() );
  wxString   prj     = ( project ? project->GetTitle() : _T( "*NONE*" ) );
  const ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>( event.GetInt() );
  switch( state ) {
    case ParserCommon::ptCreateParser: {
      std::pair<M_Code_Project*, ParserBase*> info = GetParserInfoByCurrentEditor();
      if( info.second && m_Parser != info.second ) {
        SwitchParser( info.first, info.second );
      }
    }
    break;
    case ParserCommon::ptAddFileToParser:
      break;
    case ParserCommon::ptReparseFile:
      break;
    case ParserCommon::ptUndefined:
      return;
    default:
      break;
  }
  event.Skip();
}

void NativeParser::OnParserEnd( wxCommandEvent& event ) {
  ParserBase* parser = reinterpret_cast<ParserBase*>( event.GetEventObject() );
  M_Code_Project* project = static_cast<M_Code_Project*>( event.GetClientData() );
  wxString prj = ( project ? project->GetTitle() : _T( "*NONE*" ) );
  const ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>( event.GetInt() );
  switch( state ) {
    case ParserCommon::ptCreateParser:
      break;
    case ParserCommon::ptAddFileToParser:
      break;
    case ParserCommon::ptReparseFile:
      if( parser != m_Parser ) {
        std::pair<M_Code_Project*, ParserBase*> info = GetParserInfoByCurrentEditor();
        if( info.second && info.second != m_Parser ) {
          SwitchParser( info.first, info.second );
        }
      }
      break;
    case ParserCommon::ptUndefined:
      return;
    default:
      break;
  }
  if( !event.GetString().IsEmpty() ) {
    CCLogger::Get()->DebugLog( event.GetString() );
  }
  UpdateClassBrowser();
  m_TimerParsingOneByOne.Start( 500, wxTIMER_ONE_SHOT );
  event.Skip();
}

void NativeParser::OnParsingOneByOneTimer( M_Code_unused wxTimerEvent& event ) {
  std::pair<M_Code_Project*, ParserBase*> info = GetParserInfoByCurrentEditor();
  if( m_ParserPerWorkspace ) {
    if( !info.second && Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() ) {
      AddProjectToParser( info.first );
    } else {
      M_Code_Project* activeProject = Manager::Get()->GetProjectManager()->GetActiveProject();
      if( m_ParsedProjects.find( activeProject ) == m_ParsedProjects.end() ) {
        AddProjectToParser( activeProject );
      } else {
        ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
        for( size_t i = 0; i < projs->GetCount(); ++i ) {
          if( m_ParsedProjects.find( projs->Item( i ) ) == m_ParsedProjects.end() ) {
            if( !AddProjectToParser( projs->Item( i ) ) ) {
              continue;
            }
            break;
          }
        }
      }
    }
  } else if( info.first && !info.second ) {
    info.second = CreateParser( info.first );
    if( info.second && info.second != m_Parser ) {
      SwitchParser( info.first, info.second );
    }
  }
}

void NativeParser::OnEditorActivated( EditorBase* editor ) {
  M_Code_Editor* curEditor = Manager::Get()->GetEditorManager()->GetBuiltinEditor( editor );
  if( !curEditor ) {
    return;
  }
  const wxString& activatedFile = editor->GetFilename();
  if( !wxFile::Exists( activatedFile ) ) {
    return;
  }
  M_Code_Project* project = GetProjectByEditor( curEditor );
  const int pos = m_StandaloneFiles.Index( activatedFile );
  if( project && pos != wxNOT_FOUND ) {
    m_StandaloneFiles.RemoveAt( pos );
    if( m_StandaloneFiles.IsEmpty() ) {
      DeleteParser( NULL );
    } else {
      RemoveFileFromParser( NULL, activatedFile );
    }
  }
  ParserBase* parser = GetParserByProject( project );
  if( !parser ) {
    ParserCommon::EFileType ft = ParserCommon::FileType( activatedFile );
    if( ft != ParserCommon::ftOther && ( parser = CreateParser( project ) ) ) {
      if( !project && AddFileToParser( project, activatedFile, parser ) ) {
        wxFileName file( activatedFile );
        parser->AddIncludeDir( file.GetPath() );
        m_StandaloneFiles.Add( activatedFile );
      }
    } else {
      parser = m_TempParser;
    }
  } else if( !project ) {
    if( !parser->IsFileParsed( activatedFile ) && m_StandaloneFiles.Index( activatedFile ) == wxNOT_FOUND
        && AddFileToParser( project, activatedFile, parser ) ) {
      wxFileName file( activatedFile );
      parser->AddIncludeDir( file.GetPath() );
      m_StandaloneFiles.Add( activatedFile );
    }
  }
  if( parser != m_Parser ) {
    SwitchParser( project, parser );
  }
  if( m_ClassBrowser ) {
    if( m_Parser->ClassBrowserOptions().displayFilter == bdfFile ) {
      m_ClassBrowser->UpdateClassBrowserView( true );
    } else if( m_ParserPerWorkspace && m_Parser->ClassBrowserOptions().displayFilter == bdfProject ) {
      m_ClassBrowser->UpdateClassBrowserView();
    }
  }
}

void NativeParser::OnEditorClosed( EditorBase* editor ) {
  wxString filename = editor->GetFilename();
  const int pos = m_StandaloneFiles.Index( filename );
  if( pos != wxNOT_FOUND ) {
    m_StandaloneFiles.RemoveAt( pos );
    if( m_StandaloneFiles.IsEmpty() ) {
      DeleteParser( NULL );
    } else {
      RemoveFileFromParser( NULL, filename );
    }
  }
}

void NativeParser::InitCCSearchVariables() {
  m_LastControl = nullptr;
  m_LastFunctionIndex = -1;
  m_LastLine = -1;
  m_LastResult = -1;
  m_LastFile.Clear();
  m_LastNamespace.Clear();
  m_LastPROC.Clear();
  Reset();
}

bool NativeParser::AddProjectToParser( M_Code_Project* project ) {
  wxString prj = ( project ? project->GetTitle() : _T( "*NONE*" ) );
  ParserBase* parser = GetParserByProject( project );
  if( parser ) {
    return false;
  }
  if( m_ParsedProjects.empty() ) {
    return false;
  }
  m_ParsedProjects.insert( project );
  parser = GetParserByProject( project );
  if( !parser ) {
    return false;
  } else if( !parser->UpdateParsingProject( project ) ) {
    m_ParsedProjects.erase( project );
    return false;
  }
  bool needParseMacros = false;
  if( !AddCompilerPredefinedMacros( project, parser ) ) {
    CCLogger::Get()->DebugLog( _T( "NativeParser::AddProjectToParser(): AddCompilerPredefinedMacros failed!" ) );
  } else {
    needParseMacros = true;
  }
  if( !AddProjectDefinedMacros( project, parser ) ) {
    CCLogger::Get()->DebugLog( _T( "NativeParser::AddProjectToParser(): AddProjectDefinedMacros failed!" ) );
  } else {
    if( !needParseMacros ) {
      needParseMacros = true;
    }
  }
  if( project ) {
    size_t fileCount = 0;
    for( FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it ) {
      ProjectFile* pf = *fl_it;
      if( pf && FileTypeOf( pf->relativeFilename ) == ftHeader ) {
        if( AddFileToParser( project, pf->file.GetFullPath(), parser ) ) {
          ++fileCount;
        }
      }
    }
    for( FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it ) {
      ProjectFile* pf = *fl_it;
      if( pf && ( FileTypeOf( pf->relativeFilename ) == ftSource || FileTypeOf( pf->relativeFilename ) == ftTemplateSource ) ) {
        if( AddFileToParser( project, pf->file.GetFullPath(), parser ) ) {
          fileCount++;
        }
      }
    }
    return ( ( fileCount > 0 ) || needParseMacros );
  } else {
    EditorBase* editor = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if( editor && AddFileToParser( project, editor->GetFilename(), parser ) ) {
      wxFileName file( editor->GetFilename() );
      parser->AddIncludeDir( file.GetPath() );
      m_StandaloneFiles.Add( editor->GetFilename() );
      return true;
    }
  }
  return false;
}

bool NativeParser::RemoveProjectFromParser( M_Code_Project* project ) {
  ParserBase* parser = GetParserByProject( project );
  if( !parser ) {
    return false;
  }
  m_ParsedProjects.erase( project );
  if( !project || m_ParsedProjects.empty() ) {
    return true;
  }
  for( FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it ) {
    ProjectFile* pf = *fl_it;
    if( pf && ParserCommon::FileType( pf->relativeFilename ) != ParserCommon::ftOther ) {
      RemoveFileFromParser( project, pf->file.GetFullPath() );
    }
  }
  return true;
}
